#pragma once
#include "Mode.h"
namespace yzrilyzr_crypto{
	ECLASS(PCBC, public Mode){

	private:
	yzrilyzr_array::ByteArray vector;
	yzrilyzr_array::ByteArray prevPlaintext;
	yzrilyzr_array::ByteArray prevCiphertext;
	yzrilyzr_array::ByteArray temp;
	public:
	PCBC(yzrilyzr_array::ByteArray iv){
		this->vector=yzrilyzr_array::ByteArray(iv.length);
		this->prevPlaintext=yzrilyzr_array::ByteArray(iv.length);
		this->prevCiphertext=yzrilyzr_array::ByteArray(iv.length);
		this->temp=yzrilyzr_array::ByteArray(iv.length);
		setVector(iv);
	}
	void setVector(yzrilyzr_array::ByteArray block){
		setVector(block, 0, block.length);
	}
	private:
	void setVector(yzrilyzr_array::ByteArray block, u_index off, u_index len){
		yzrilyzr_lang::System::arraycopy(block, off, vector, 0, yzrilyzr_lang::Math::min(len, vector.length));
		if(block.length == vector.length){
			yzrilyzr_lang::System::arraycopy(block, off, prevCiphertext, 0, vector.length);
		}
	}

	public:
	yzrilyzr_array::ByteArray encrypt(const yzrilyzr_array::ByteArray & block, FAQCipherAlgorithm & algorithm)override{
				// XOR plaintext with (previous plaintext XOR previous ciphertext)
		CryptoUtil:: xor (prevPlaintext, prevCiphertext, temp);
		CryptoUtil:: xor (block, temp, temp);
		// Encrypt the result
		yzrilyzr_array::ByteArray ciphertext=algorithm.encrypt(temp);
		// Update state
		yzrilyzr_lang::System::arraycopy(temp, 0, prevPlaintext, 0, temp.length);
		yzrilyzr_lang::System::arraycopy(ciphertext, 0, prevCiphertext, 0, ciphertext.length);
		return ciphertext;
	}
	yzrilyzr_array::ByteArray encryptFinal(const yzrilyzr_array::ByteArray & block, FAQCipherAlgorithm & algorithm)override{
		return encrypt(block, algorithm);
	}
	yzrilyzr_array::ByteArray decrypt(const yzrilyzr_array::ByteArray & block, FAQCipherAlgorithm & algorithm)override{
		   // Decrypt the ciphertext
		yzrilyzr_array::ByteArray decrypted=algorithm.decrypt(block);
		// XOR with (previous plaintext XOR previous ciphertext)
		CryptoUtil:: xor (prevPlaintext, prevCiphertext, temp);
		CryptoUtil:: xor (decrypted, temp, temp);
		// Update state
		yzrilyzr_lang::System::arraycopy(temp, 0, prevPlaintext, 0, temp.length);
		yzrilyzr_lang::System::arraycopy(block, 0, prevCiphertext, 0, block.length);
		return temp;
	}
	yzrilyzr_array::ByteArray decryptFinal(const yzrilyzr_array::ByteArray & block, FAQCipherAlgorithm & algorithm)override{
		return decrypt(block, algorithm);
	}
	};
}